﻿using System;
using System.Configuration;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using WU.LoadTester.Lib.Service;
using WU.LoadTester.UI.Properties;
using Settings = WU.LoadTester.Lib.Settings;

namespace WU.LoadTester.UI
{
    public partial class MainForm : Form
    {
        public delegate void IncrementProgress(int i);
        public delegate void Toggle(bool b);
        private Lib.Program _program = new Lib.Program();
        private Settings _settings = new Settings();
        private CancellationTokenSource _source;
        private bool _running;
        private bool _isOutputting = false;

        public MainForm()
        {
            InitializeComponent();
        }


        #region EVENTS

        #region tooltip_menu_items
        /// <summary>
        /// Safe exit
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_running)
            {
                var result = MessageBox.Show(Resources.MainForm_exitToolStripMenuItem_Click_A_test_is_running__are_you_sure_, Resources.MainForm_exitToolStripMenuItem_Click_Exit, MessageBoxButtons.OKCancel);
                if (result == DialogResult.OK)
                {
                    Stop();
                    Application.Exit();
                }
            }
            else
            {
                Application.Exit();
            }
        }

        /// <summary>
        /// About the program
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsiAbout_Click(object sender, EventArgs e)
        {
            string about = String.Format(Resources.About_Program, this.ProductVersion, Settings.ReleaseString);
            MessageBox.Show(about);
        }
        #endregion

        private void MainForm_Load(object sender, EventArgs e)
        {
            //Get pre-loadded values from app.config
            foreach (var key in ConfigurationManager.AppSettings.AllKeys)
            {
                if (key == "FavoriteEditorPath")
                {
                    _settings.FavoriteEditorPath = key;
                    continue;
                }

                var controlKey = string.Format("txt{0}", key);
                var controls = this.Controls.Find(controlKey, true);
                if (controls.Length >= 1)
                {
                    var textbox = controls[0] as TextBox;
                    if (textbox != null)
                        textbox.Text = ConfigurationManager.AppSettings[key];
                }
            }
        }

        private void btnBrowse_Click(object sender, EventArgs e)
        {
            //Initaliaze
            var dialog = new OpenFileDialog();
            dialog.AddExtension = true;
            dialog.CheckFileExists = true;
            dialog.CheckPathExists = true;
            dialog.Multiselect = false;
            dialog.DefaultExt = "csv";
            dialog.Title = Resources.MainForm_btnBrowse_Click_Select_your_input_file;
            
            //Check result
            var result = dialog.ShowDialog();
            if(result == DialogResult.OK)
            {
                var filePath = dialog.FileName;
                txtCSVPath.Text = filePath;
            } 
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            //Check required parameters
            if (this.txtCSVPath.Text == string.Empty)
            {
                MessageBox.Show(Resources.MainForm_btnStart_Click_Fill_out_a_valid_CSV_File_path_to_start);
                return;
            }
            if (!this.chkCallsEntireFile.Checked && !this.chkCallsUnlimited.Checked && this.txtCalls.Text.Length == 0)
            {
                MessageBox.Show(Resources.MainForm_btnStart_Click_Select_a_test_type__unlimited_calls_or_entire_file);
                return;
            }

            //Parameters ok, setup settings
            _settings.ClientType = this.GetClientType();
            _settings.CSVFilepath = this.txtCSVPath.Text;
            _settings.EndpointAddress = this.txtAddress.Text;
            _settings.Logging = this.chkLogResults.Checked;
            _settings.LogFilePath = Environment.GetEnvironmentVariable("USERPROFILE");
            _settings.Threads = this.txtThreads.Text != string.Empty ? int.Parse(this.txtThreads.Text) : 1;

            //Set calls and call type
            if(chkCallsEntireFile.Checked || chkCallsUnlimited.Checked)
            {
                _settings.CallType = this.chkCallsEntireFile.Checked ? Settings.CallsType.Entire : Settings.CallsType.Unlimited;
            }
            else
            {
                int calls;
                if (!int.TryParse(this.txtCalls.Text, out calls))
                {
                    MessageBox.Show(Resources.MainForm_btnStart_Click_Entire_a_valid_number_of_calls);
                    return;
                }
                _settings.CallType = Settings.CallsType.Fixed;
                _settings.Calls = calls;
            }
            
            
            try
            {
                //Bind to the log event to display output
                if (!_isOutputting)
                {
                    Lib.LogFile.OnWrite += Output;
                    _isOutputting = true;
                }

                //Settings ok, launch main
                ToggleAllButtons(false);
                ToggleInputs(false);
                btnReset.Enabled = true;
                btnStop.Enabled = true;

                //Last minute validations
                if (_settings.Threads > _program.Data.Matrix.Count)
                {
                    MessageBox.Show(Resources.Error_NotEnoughRows);
                    return;
                }

                pbProgress.Value = 0;
                _source = new CancellationTokenSource();

                //Set the progress bar
                if (_settings.CallType == Settings.CallsType.Entire)
                    this.pbProgress.Maximum = _program.Data.Matrix.Count;
                else if (_settings.CallType == Settings.CallsType.Fixed)
                    this.pbProgress.Maximum = _settings.Calls;


                //Start the worker and wait
                var task = new Task(()=> Run(_source.Token), _source.Token);
                task.Start();
                _running = true;
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.InnerException != null
                                    ? String.Format("Exception:{0} \n \n Stack Trace: {1} \n \n Inner Exception: {2}",
                                                    ex.Message, ex.StackTrace, ex.InnerException.Message)
                                    : String.Format("Exception: {0} \n \n Stack Trace: {1}", ex.Message, ex.StackTrace));
                ToggleInputs(false);
                btnReset.Enabled = true;
                btnStop.Enabled = true;
            }
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            if (this.txtAddress.Text == string.Empty)
            {
                MessageBox.Show(Resources.MainForm_btnLoad_Click_Enter_the_endpoint_address_to_start);
                return;
            }

            // AUTH IS HANDLED IN BINDINGS FOR WCF SERVICE CLIENTS
            _settings.ClientType = GetClientType();
            if (GetClientType() == Settings.ClientsType.Soap)
            {

                if (GetSelectedBinding() == BindingType.None)
                {
                    MessageBox.Show(Resources.MainForm_btnLoad_Click_Please_select_a_binding_before_loading_the_service);
                    return;
                }
            }
            // AUTH IS HANDLED BY THE FROM FOR REST SERVICE CLIENTS
            else
            {
                //Check authentication
                _settings.AuthenticationSettings = this.GetAuth();
                switch (_settings.AuthenticationSettings.AuthenticationType)
                {
                    case AuthType.Acs:
                        if (string.IsNullOrEmpty(txtRealm.Text)
                            || string.IsNullOrEmpty(txtIdentity.Text)
                            || string.IsNullOrEmpty(txtKey.Text))
                            MessageBox.Show(Resources.Message_AcsInfoMissing);
                        return;
                    case AuthType.Digest:
                        if (string.IsNullOrEmpty(txtUsername.Text) || string.IsNullOrEmpty(txtPassword.Text))
                            MessageBox.Show(Resources.Message_UsernamePasswordMissing);
                        return;
                }
            }

            try
            {
                //LOAD SETTINGS
                _program.Load(_settings);

                //LOAD SERVICE
                if (_settings.ClientType == Settings.ClientsType.Rest)
                    _program.LoadWebClient(txtAddress.Text, _settings.AuthenticationSettings);
                else if (_settings.ClientType == Settings.ClientsType.Soap)
                    _program.LoadService(txtAddress.Text, GetSelectedBinding());
                else
                {
                    MessageBox.Show(Resources.Error_Client);
                    return;
                }

                //Settings ok, launch main
                ToggleAllButtons(false);
                ToggleInputs(false);
                if (_settings.ClientType == Settings.ClientsType.Rest)
                    gbAuth.Enabled = true;

                gbCalls.Enabled = true;
                gbOptions.Enabled = true;
                btnStart.Enabled = true;
                btnStop.Enabled = true;
                btnReset.Enabled = true;
                btnTest.Enabled = true;

                txtOutput.Text += String.Format(Resources.Message_EndpointLoaded, txtAddress.Text);
                txtCalls.Focus();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.InnerException != null
                                    ? String.Format("Exception:{0} \n \n Stack Trace: {1} \n \n Inner Exception: {2}",
                                                    ex.Message, ex.StackTrace, ex.InnerException.Message)
                                    : String.Format("Exception: {0} \n \n Stack Trace: {1}", ex.Message, ex.StackTrace));


                Reset();
            }
        }
        private void btnReset_Click(object sender, EventArgs e)
        {
            Reset();
        }

        private void rbAuthACS_CheckedChanged(object sender, EventArgs e)
        {
            var button = sender as RadioButton;
            if (button != null && button.Checked)
            {
                this.txtRealm.Enabled = true;
                this.txtIdentity.Enabled = true;
                this.txtName.Enabled = true;
                this.txtKey.Enabled = true;
            }
            else
            {
                this.txtRealm.Enabled = false;
                this.txtIdentity.Enabled = false;
                this.txtName.Enabled = false;
                this.txtKey.Enabled = false;
            }
        }

        private void rbAuthDigest_CheckedChanged(object sender, EventArgs e)
        {
            var button = sender as RadioButton;
            if (button != null && button.Checked)
            {
                this.txtUsername.Enabled = true;
                this.txtPassword.Enabled = true;
            }
            else
            {
                this.txtUsername.Enabled = false;
                this.txtPassword.Enabled = false;
            }
        }

        private void btnTest_Click(object sender, EventArgs e)
        {
            // Test Auth
            var auth = GetAuth();
            _settings.EndpointAddress = txtAddress.Text;

            try
            {
                var b = _program.TestAuthenticationSettings(_settings.EndpointAddress, auth);
                txtOutput.Text += b ? Resources.Auth_OK : Resources.Auth_Unauthorized;
                txtOutput.Text += Environment.NewLine;
            }
            catch (Exception x)
            {
                MessageBox.Show(String.Format(Resources.Error_Authentication, x.Message));
                txtOutput.Text += x.Message + Environment.NewLine;
                txtOutput.Text += x.StackTrace + Environment.NewLine;
            }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            Stop();
        }

        private void rbSoap_CheckedChanged(object sender, EventArgs e)
        {
            if (rbSoap.Checked)
                this.gbBindings.Enabled = true;
            else
            {
                this.gbBindings.Enabled = false;
            }
        }

        private void chkCalls_CheckedChanged(object sender, EventArgs e)
        {
            if (chkCallsEntireFile.Checked || chkCallsUnlimited.Checked)
                txtCalls.Enabled = false;
            else
                txtCalls.Enabled = true;
        }

        private void rbJson_CheckedChanged(object sender, EventArgs e)
        {
            this.gbAuth.Enabled = rbJson.Checked;
        }

        #endregion

        #region Privates

        private BindingType GetSelectedBinding()
        {
            if (rbBasicHttp.Checked)
                return BindingType.BasicHttpBinding;
            if (rbWsHttpBinding.Checked)
                return BindingType.WsHttpBinding;
            if (rbNetTcpBinding.Checked)
                return BindingType.NetTCPBinding;
            if(rbAsmxBinding.Checked)
                return BindingType.AsmxBinding;
            return BindingType.None;
        }

        private AuthSettings GetAuth()
        {
            var auth = new AuthSettings();
            if(rbAuthIntegrated.Checked)
                auth.AuthenticationType = AuthType.Integrated;
            if (rbAuthBasic.Checked)
                auth.AuthenticationType = AuthType.Basic;
            if (rbAuthDigest.Checked)
                auth.AuthenticationType = AuthType.Digest;
            if (rbAuthACS.Checked)
            {
                auth.AuthenticationType = AuthType.Acs;
                auth.ACSName = txtName.Text;
                auth.IdentityName = txtIdentity.Text;
                auth.ProviderRealm = txtRealm.Text;
                auth.SymetricKey = txtKey.Text;
            }

            auth.Password = txtPassword.Text;
            auth.Username = txtUsername.Text;
            return auth;
        }

        private Settings.ClientsType GetClientType()
        {
            if (rbJson.Checked)
                return Settings.ClientsType.Rest;
            if (rbSoap.Checked)
                return Settings.ClientsType.Soap;
            return Settings.ClientsType.None;
        }

        private void Stop()
        {
            if (_source != null)
            {
                _source.Cancel();
                txtOutput.Text += String.Format("\n{0}",Resources.Message_Stopped);
            }
            _running = false;

            txtCSVPath.Enabled = true;
            btnBrowse.Enabled = true;

            gbCalls.Enabled = true;
            gbOptions.Enabled = true;

            btnStart.Enabled = true;
            btnReset.Enabled = true;
        }

        /// <summary>
        /// Stop all tests and reset settings
        /// </summary>
        private void Reset()
        {
            if (_source != null)
            {
                _source.Cancel();
                txtOutput.Text += String.Format("\n{0}", Resources.Message_Stopped);
            }

            //Enable and reset
            ToggleInputs(false);
            ResetInputs();

            ToggleInputs(true);
            _running = false;
        }
        private void ToggleInputs(bool enable)
        {
            //Toggle textboxes
            txtAddress.Enabled = enable;
            txtCSVPath.Enabled = enable;

            //Toggle groups
            gbType.Enabled = enable;
            gbBindings.Enabled = enable;
            gbCalls.Enabled = enable;
            gbOptions.Enabled = enable;
            gbAuth.Enabled = enable;
        }

        private void ResetInputs()
        {
            txtAddress.Clear();
            txtCSVPath.Clear();
            txtOutput.Clear();
            txtCalls.Clear();
            txtThreads.Clear();

            txtUsername.Clear();
            txtPassword.Clear();
            txtRealm.Clear();
            txtIdentity.Clear();
            txtName.Clear();
            txtKey.Clear();

            rbSoap.Checked = false;
            rbJson.Checked = false;
            rbAsmxBinding.Checked = false;
            rbBasicHttp.Checked = false;
            rbNetTcpBinding.Checked = false;
            rbWsHttpBinding.Checked = false;

            rbAuthACS.Checked = false;
            rbAuthBasic.Checked = false;
            rbAuthDigest.Checked = false;
            rbAuthIntegrated.Checked = false;

            chkCallsEntireFile.Checked = false;
            chkCallsUnlimited.Checked = false;
            chkLogResults.Checked = false;

            txtCSVPath.Focus();
        }

        private void Run(CancellationToken token)
        {
            _program.Execute(Increment, token);
            ToggleAllButtons(true);
            _running = false;
            if (_settings.Logging)
            {
                MessageBox.Show(String.Format(Resources.Message_ExecutionComplete,
                      _settings.LogFilePath));
                _program.DisplayResults();
            }
            else
            {
                MessageBox.Show(Resources.MainForm_Run_Execution_is_completed);
            }
        }

        private void Increment(int i)
        {
            if (this.pbProgress.InvokeRequired)
            {
                IncrementProgress increment = new IncrementProgress(Increment);
                this.Invoke(increment, new object[] { i });
            }
            else
            {
                if (this._settings.CallType == Settings.CallsType.Unlimited)
                {
                    this.pbProgress.Value = 0;
                }
                else
                {
                    this.pbProgress.Value += i;
                }
            }
        }

        private void Output(string text)
        {
            if(this.txtOutput.InvokeRequired)
            {
                var output = new Action<string>(Output);
                this.Invoke(output, new object[] {text});
            }
            else
            {
                this.txtOutput.Text += Environment.NewLine + text;
            }
        }

        private void ToggleAllButtons(bool enable)
        {
            if (btnReset.InvokeRequired)
            {
                Toggle toggle = new Toggle(ToggleAllButtons);
                this.Invoke(toggle, new object[] { enable });
            }
            else
            {

                foreach (var control in this.Controls)
                {
                    if (control.GetType() == typeof(Button))
                    {
                        ((Button)control).Enabled = enable;
                    }
                }
            }
        }
        #endregion
    }
}
